/**
 * 
 */
package hu.cubussapiens.facts.query.xml;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import hu.cubussapiens.facts.query.And;
import hu.cubussapiens.facts.query.Construct;
import hu.cubussapiens.facts.query.CreateLiteral;
import hu.cubussapiens.facts.query.Literal;
import hu.cubussapiens.facts.query.Or;
import hu.cubussapiens.facts.query.Query;
import hu.cubussapiens.facts.query.Select;

/**
 * @author balage
 *
 */
public class QueryParser {

	private static final Logger log = Logger.getLogger(QueryParser.class.getName());
	
	public static final List<Query> parse(InputStream is) throws ParserConfigurationException, SAXException, IOException{
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
	
		Document doc = builder.parse(is);
		
		List<Query> result = new ArrayList<Query>();
		
		Element q = doc.getDocumentElement();
		if ("query".equals(q.getNodeName())){
			NodeList nl = q.getChildNodes();
			for(int i = 0;i<nl.getLength();i++){
				Query q1 = parseOne(nl.item(i));
				if (q1 != null)
					result.add(q1);
			}
		}
		
		return result;
	}
	
	public static final Query parseOne(Node node){
		if (node instanceof Element){
			Element e = (Element)node;
			log.info("Parsing: "+e.getNodeName());
			if ("literal".equals(e.getNodeName())){
				String l = e.getAttribute("data");
				if (!"".equals(l))
					return new Literal(l);
			}else
			if ("createliteral".equals(e.getNodeName())){
				String l = e.getAttribute("data");
				Long ra = Long.parseLong(e.getAttribute("access"));
				if (!"".equals(l))
					return new CreateLiteral(l,ra);
			}else
			if ("construct".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				if (nl.size() == 3){
					Query q1 = parseOne(nl.get(0));
					Query q2 = parseOne(nl.get(1));
					Query q3 = parseOne(nl.get(2));
					return new Construct(q1, q2, q3);
				}
			}else
			if ("selectObject".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				if (nl.size() == 2){
					Query q1 = parseOne(nl.get(0));
					Query q2 = parseOne(nl.get(1));
					return new Select(q1, q2, 0);
				}
			}else
			if ("selectVerb".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				if (nl.size() == 2){
					Query q1 = parseOne(nl.get(0));
					Query q2 = parseOne(nl.get(1));
					return new Select(q1, q2, 1);
				}
			}else
			if ("selectSubject".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				if (nl.size() == 2){
					Query q1 = parseOne(nl.get(0));
					Query q2 = parseOne(nl.get(1));
					return new Select(q1, q2, 2);
				}
			}else
			if ("and".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				List<Query> qs = new ArrayList<Query>(nl.size());
				for(Element q : nl){
					qs.add(parseOne(q));
				}
				return new And(qs);
			}else
			if ("or".equals(e.getNodeName())){
				List<Element> nl = getChildElements(e);
				List<Query> qs = new ArrayList<Query>(nl.size());
				for(Element q : nl){
					qs.add(parseOne(q));
				}
				return new Or(qs);
			}else
				log.severe("Unknown element: "+e.getNodeName());
		}else{
			log.severe("Parsing non-element: "+node.getNodeType()+" = '"+node.getNodeValue()+"'");
		}
		log.warning("Returning null!");
		return null;
	}
	
	private static List<Element> getChildElements(Element e){
		List<Element> es = new ArrayList<Element>();
		NodeList nl = e.getChildNodes();
		for(int i = 0; i<nl.getLength() ;i++){
			Node n = nl.item(i);
			if (n instanceof Element)
				es.add((Element)n);
		}
		return es;
	}
	
}
